package com.fanco.learn_datastruct_leetcode.juc;

/**
 * @author fanco
 * @Desc 读写锁 Demo
 * 实现一个读写缓存的操作， 读时可以多个线程一起读  写时 只能一个线程写
 */

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 资源类
 */
class Cache {

    private volatile Map<String, Object> map = new HashMap<>(8);

    private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    public void write(String key, Object value) {
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "\t" + "正在写入。。。。。。");
            try {
                // 模拟网络拥堵，写操作延时0.3s
                TimeUnit.MILLISECONDS.sleep(300);
            } catch (Exception e) {
                e.printStackTrace();
            }
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "\t" + "写入完成");
        } finally {
            readWriteLock.writeLock().unlock();
        }
    }

    public void read(String key) {
        readWriteLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "\t" + "正在读取");
            try {
                // 模拟网络拥堵，写操作延时0.3s
                TimeUnit.MILLISECONDS.sleep(500);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "读取后的值为" + map.get(key));
        } finally {
            readWriteLock.readLock().unlock();
        }
    }
}

/**
 * @author Fanco
 */
public class ReadWriteLockDemo {
    public static void main(String[] args) {
        Cache cache = new Cache();

        // 五个线程写
        for (int i = 0; i < 5; i++) {
            int finalI = i;
            new Thread(() -> {
                cache.write(finalI + "", "Test");
            }, "写线程" + i).start();
        }

        // 主线程 方便 看最后结果
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 五个线程读
        for (int i = 0; i < 5; i++) {
            int finalI = i;
            new Thread(() -> {
                cache.read(finalI + "");
            }, "读线程" + i).start();
        }

        // 多个线程同时 对 缓存读写时， 如果不加锁，会造成写线程 还未写入，读线程已经开始读取了
        // 解决方案： 增加 读写锁， 写缓存 时 增加 写锁， 只能一个线程进行写操作  读缓存 时 增加 读锁，可以多个线程同时读取
    }
}
